import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from wordcloud import WordCloud
from collections import Counter
import plotly.express as px
import tkinter as tk
from tkinter import ttk, messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import hashlib
import json
import os
from PIL import Image, ImageTk

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统
plt.rcParams['axes.unicode_minus'] = False


class LoginSystem:
    def __init__(self, root):
        self.root = root
        self.root.title("AI职位分析系统 - 登录")
        self.root.geometry("1000x700")
        self.root.configure(bg='#f0f2f5')

        # 用户数据文件
        self.users_file = "users.json"

        # 创建用户数据文件如果不存在
        if not os.path.exists(self.users_file):
            with open(self.users_file, 'w') as f:
                json.dump({}, f)

        # 加载数据
        self.df = self.load_data()

        # 创建登录界面
        self.create_login_interface()

    def load_data(self):
        # 这里可以改为从数据库或API加载数据
        try:
            df = pd.read_csv('ai_job_dataset.csv')

            # 货币转换（假设当前汇率）
            exchange_rates = {'USD': 1, 'EUR': 1.07, 'GBP': 1.25}
            df['salary_usd_converted'] = df.apply(
                lambda x: x['salary_usd'] * exchange_rates.get(x['salary_currency'], 1), axis=1)

            # 公司规模编码
            size_mapping = {'S': 0, 'M': 1, 'L': 2}
            df['company_size_encoded'] = df['company_size'].map(size_mapping)

            return df
        except Exception as e:
            messagebox.showerror("错误", f"加载数据失败: {str(e)}")
            return pd.DataFrame()

    def hash_password(self, password):
        """使用SHA-256哈希密码"""
        return hashlib.sha256(password.encode()).hexdigest()

    def register_user(self, username, password, email):
        """注册新用户"""
        try:
            with open(self.users_file, 'r') as f:
                users = json.load(f)

            if username in users:
                messagebox.showerror("错误", "用户名已存在")
                return False

            users[username] = {
                'password': self.hash_password(password),
                'email': email,
                'registration_date': pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S")
            }

            with open(self.users_file, 'w') as f:
                json.dump(users, f, indent=4)

            messagebox.showinfo("成功", "注册成功！请登录")
            return True
        except Exception as e:
            messagebox.showerror("错误", f"注册失败: {str(e)}")
            return False

    def authenticate(self, username, password):
        """验证用户登录"""
        try:
            with open(self.users_file, 'r') as f:
                users = json.load(f)

            if username in users and users[username]['password'] == self.hash_password(password):
                return True
            return False
        except Exception as e:
            messagebox.showerror("错误", f"登录失败: {str(e)}")
            return False

    def create_login_interface(self):
        """创建紧凑型登录界面"""
        for widget in self.root.winfo_children():
            widget.destroy()

        self.root.geometry("400x350")
        self.root.configure(bg='#f0f2f5')

        main_frame = tk.Frame(self.root, bg='#f0f2f5')
        main_frame.pack(expand=True, padx=30, pady=20)

        # 标题
        title_label = tk.Label(main_frame, text="AI职位分析系统",
                               font=("Microsoft YaHei", 18, "bold"),
                               bg='#f0f2f5', fg='#1877f2')
        title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15))

        # 用户名
        username_label = tk.Label(main_frame, text="用户名:",
                                  font=("Microsoft YaHei", 10),
                                  bg='#f0f2f5')
        username_label.grid(row=1, column=0, sticky='e', padx=(0, 5))

        self.username_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                       relief='flat', highlightthickness=1,
                                       highlightbackground='#ddd', highlightcolor='#1877f2')
        self.username_entry.grid(row=1, column=1, sticky='ew', ipady=3)  # ipady在grid方法中使用

        # 密码
        password_label = tk.Label(main_frame, text="密码:",
                                  font=("Microsoft YaHei", 10),
                                  bg='#f0f2f5')
        password_label.grid(row=2, column=0, sticky='e', padx=(0, 5), pady=(5, 0))

        self.password_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                       show="*", relief='flat', highlightthickness=1,
                                       highlightbackground='#ddd', highlightcolor='#1877f2')
        self.password_entry.grid(row=2, column=1, sticky='ew', ipady=3, pady=(5, 0))

        # 第四行：登录按钮
        login_btn = tk.Button(main_frame, text="登录",
                              command=self.handle_login,
                              font=("Microsoft YaHei", 14, "bold"),
                              bg='#1877f2', fg='white',
                              activebackground='#166fe5', activeforeground='white',
                              relief='flat', bd=0, padx=20, pady=10)
        login_btn.grid(row=3, column=0, columnspan=2, sticky='ew', pady=(10, 5))

        # 第五行：注册链接
        register_frame = tk.Frame(main_frame, bg='#f0f2f5')
        register_frame.grid(row=4, column=0, columnspan=2)

        tk.Label(register_frame, text="没有账号?",
                 font=("Microsoft YaHei", 10),
                 bg='#f0f2f5').pack(side='left')

        register_link = tk.Label(register_frame, text="立即注册",
                                 font=("Microsoft YaHei", 10, "underline"),
                                 fg='#1877f2', cursor="hand2", bg='#f0f2f5')
        register_link.pack(side='left')
        register_link.bind("<Button-1>", lambda e: self.show_register_form())

        # 配置网格列权重
        main_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_columnconfigure(1, weight=3)

        # 绑定回车键登录
        self.root.bind('<Return>', lambda e: self.handle_login())

    def show_register_form(self):
        """创建紧凑型注册界面"""
        for widget in self.root.winfo_children():
            widget.destroy()

        self.root.geometry("400x450")
        self.root.configure(bg='#f0f2f5')

        main_frame = tk.Frame(self.root, bg='#f0f2f5')
        main_frame.pack(expand=True, padx=30, pady=20)

        # 标题
        title_label = tk.Label(main_frame, text="用户注册",
                               font=("Microsoft YaHei", 18, "bold"),
                               bg='#f0f2f5', fg='#1877f2')
        title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15))

        # 用户名
        username_label = tk.Label(main_frame, text="用户名:",
                                  font=("Microsoft YaHei", 10),
                                  bg='#f0f2f5')
        username_label.grid(row=1, column=0, sticky='e', padx=(0, 5))

        self.reg_username_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                           relief='flat', highlightthickness=1,
                                           highlightbackground='#ddd', highlightcolor='#1877f2')
        self.reg_username_entry.grid(row=1, column=1, sticky='ew', ipady=3, pady=(0, 5))

        # 邮箱
        email_label = tk.Label(main_frame, text="邮箱:",
                               font=("Microsoft YaHei", 10),
                               bg='#f0f2f5')
        email_label.grid(row=2, column=0, sticky='e', padx=(0, 5))

        self.email_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                    relief='flat', highlightthickness=1,
                                    highlightbackground='#ddd', highlightcolor='#1877f2')
        self.email_entry.grid(row=2, column=1, sticky='ew', ipady=3, pady=(0, 5))

        # 密码
        password_label = tk.Label(main_frame, text="密码:",
                                  font=("Microsoft YaHei", 10),
                                  bg='#f0f2f5')
        password_label.grid(row=3, column=0, sticky='e', padx=(0, 5))

        self.reg_password_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                           show="*", relief='flat', highlightthickness=1,
                                           highlightbackground='#ddd', highlightcolor='#1877f2')
        self.reg_password_entry.grid(row=3, column=1, sticky='ew', ipady=3, pady=(0, 5))

        # 确认密码
        confirm_label = tk.Label(main_frame, text="确认密码:",
                                 font=("Microsoft YaHei", 10),
                                 bg='#f0f2f5')
        confirm_label.grid(row=4, column=0, sticky='e', padx=(0, 5))

        self.confirm_password_entry = tk.Entry(main_frame, font=("Microsoft YaHei", 10),
                                               show="*", relief='flat', highlightthickness=1,
                                               highlightbackground='#ddd', highlightcolor='#1877f2')
        self.confirm_password_entry.grid(row=4, column=1, sticky='ew', ipady=3, pady=(0, 5))

        # 注册按钮
        register_btn = tk.Button(main_frame, text="注册",
                                 command=self.handle_register,
                                 font=("Microsoft YaHei", 12, "bold"),
                                 bg='#1877f2', fg='white',
                                 activebackground='#166fe5', activeforeground='white',
                                 relief='flat', bd=0)
        register_btn.grid(row=5, column=0, columnspan=2, sticky='ew', pady=(10, 5))

        # 返回登录链接
        back_frame = tk.Frame(main_frame, bg='#f0f2f5')
        back_frame.grid(row=6, column=0, columnspan=2, pady=(0, 5))

        back_link = tk.Label(back_frame, text="返回登录",
                             font=("Microsoft YaHei", 9, "underline"),
                             fg='#1877f2', cursor="hand2", bg='#f0f2f5')
        back_link.pack(side='right')
        back_link.bind("<Button-1>", lambda e: self.create_login_interface())

        # 网格配置
        main_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_columnconfigure(1, weight=3)

        # 绑定回车键注册
        self.root.bind('<Return>', lambda e: self.handle_register())

    def handle_login(self):
        """处理登录请求"""
        username = self.username_entry.get()
        password = self.password_entry.get()

        if not username or not password:
            messagebox.showerror("错误", "请输入用户名和密码")
            return

        if self.authenticate(username, password):
            self.root.unbind('<Return>')
            self.show_main_app(username)
        else:
            messagebox.showerror("错误", "用户名或密码错误")

    def handle_register(self):
        """处理注册请求"""
        username = self.reg_username_entry.get()
        email = self.email_entry.get()
        password = self.reg_password_entry.get()
        confirm_password = self.confirm_password_entry.get()

        if not username or not email or not password or not confirm_password:
            messagebox.showerror("错误", "请填写所有字段")
            return

        if password != confirm_password:
            messagebox.showerror("错误", "两次输入的密码不匹配")
            return

        if len(password) < 6:
            messagebox.showerror("错误", "密码长度至少为6位")
            return

        if self.register_user(username, password, email):
            self.create_login_interface()

    def show_main_app(self, username):
        """显示主应用程序"""
        self.root.unbind('<Return>')
        for widget in self.root.winfo_children():
            widget.destroy()

        # 创建主应用程序
        AIJobAnalyzerApp(self.root, self.df, username)


class AIJobAnalyzerApp:
    def __init__(self, root, df, username):
        self.root = root
        self.df = df
        self.username = username
        self.root.title(f"AI职位数据分析可视化工具 - 欢迎 {username}")
        self.root.geometry("1000x700")
        self.root.configure(bg='#f5f5f5')

        # 创建主界面
        self.create_main_interface()

    def create_main_interface(self):
        # 清除现有内容
        for widget in self.root.winfo_children():
            widget.destroy()

        # 顶部导航栏
        nav_frame = tk.Frame(self.root, bg='#1877f2', height=60)
        nav_frame.pack(fill='x', side='top')

        # 标题
        title_label = tk.Label(nav_frame, text="AI职位数据分析可视化工具",
                               font=("Microsoft YaHei", 18, "bold"),
                               bg='#1877f2', fg='white')
        title_label.pack(side='left', padx=20)

        # 用户信息和退出按钮
        user_frame = tk.Frame(nav_frame, bg='#1877f2')
        user_frame.pack(side='right', padx=20)

        user_label = tk.Label(user_frame, text=f"用户: {self.username}",
                              font=("Microsoft YaHei", 12),
                              bg='#1877f2', fg='white')
        user_label.pack(side='left', padx=10)

        logout_btn = tk.Button(user_frame, text="退出",
                               command=self.logout,
                               font=("Microsoft YaHei", 10),
                               bg='#166fe5', fg='white',
                               activebackground='#145bcb', activeforeground='white',
                               relief='flat', bd=0, padx=10)
        logout_btn.pack(side='left')

        # 主内容区域
        main_frame = tk.Frame(self.root, bg='#f5f5f5')
        main_frame.pack(expand=True, fill='both', padx=20, pady=20)

        # 欢迎信息
        welcome_frame = tk.Frame(main_frame, bg='#f5f5f5')
        welcome_frame.pack(fill='x', pady=(0, 20))

        welcome_label = tk.Label(welcome_frame,
                                 text="请选择要生成的可视化图表:",
                                 font=("Microsoft YaHei", 14),
                                 bg='#f5f5f5')
        welcome_label.pack(side='left')

        # 按钮框架
        button_frame = tk.Frame(main_frame, bg='#f5f5f5')
        button_frame.pack(expand=True, fill='both')

        # 创建按钮
        buttons = [
            ("TOP 10 AI职位薪资分布", self.show_salary_distribution),
            ("工作经验与薪资关系", self.show_exp_vs_salary),
            ("公司规模与薪资水平", self.show_company_size_salary),
            ("TOP 15 热门AI技能需求", self.show_top_skills),
            ("AI职位技能词云", self.show_skills_wordcloud),
            ("AI职位全球分布热力图", self.show_global_distribution),
            ("远程工作类型分布", self.show_remote_work),
            ("不同行业的AI职位薪资比较", self.show_industry_salary),
            ("AI职位学历要求分布", self.show_education_requirements),
            ("3D: 经验-公司规模-薪资", self.show_3d_relationship)
        ]

        # 排列按钮 - 使用网格布局
        for i, (text, command) in enumerate(buttons):
            btn = tk.Button(button_frame, text=text, command=command,
                            width=25, height=2, font=("Microsoft YaHei", 11),
                            bg='white', fg='#333',
                            activebackground='#f0f0f0', activeforeground='#333',
                            relief='groove', bd=1)
            btn.grid(row=i // 2, column=i % 2, padx=10, pady=10, sticky='nsew')

        # 配置网格行列权重
        for i in range(2):
            button_frame.grid_columnconfigure(i, weight=1)
        for i in range(5):
            button_frame.grid_rowconfigure(i, weight=1)

    def logout(self):
        """返回登录界面"""
        for widget in self.root.winfo_children():
            widget.destroy()
        LoginSystem(self.root)

    def show_figure(self, fig):
        # 清除现有内容
        for widget in self.root.winfo_children():
            widget.destroy()

        # 顶部导航栏
        nav_frame = tk.Frame(self.root, bg='#1877f2', height=60)
        nav_frame.pack(fill='x', side='top')

        # 返回按钮
        back_btn = tk.Button(nav_frame, text="返回主界面",
                             command=self.create_main_interface,
                             font=("Microsoft YaHei", 12),
                             bg='#166fe5', fg='white',
                             activebackground='#145bcb', activeforeground='white',
                             relief='flat', bd=0, padx=10)
        back_btn.pack(side='left', padx=20)

        # 用户信息和退出按钮
        user_frame = tk.Frame(nav_frame, bg='#1877f2')
        user_frame.pack(side='right', padx=20)

        user_label = tk.Label(user_frame, text=f"用户: {self.username}",
                              font=("Microsoft YaHei", 12),
                              bg='#1877f2', fg='white')
        user_label.pack(side='left', padx=10)

        logout_btn = tk.Button(user_frame, text="退出",
                               command=self.logout,
                               font=("Microsoft YaHei", 10),
                               bg='#166fe5', fg='white',
                               activebackground='#145bcb', activeforeground='white',
                               relief='flat', bd=0, padx=10)
        logout_btn.pack(side='left')

        # 显示图表
        canvas_frame = tk.Frame(self.root, bg='#f5f5f5')
        canvas_frame.pack(expand=True, fill='both', padx=20, pady=20)

        canvas = FigureCanvasTkAgg(fig, master=canvas_frame)
        canvas.draw()
        canvas.get_tk_widget().pack(expand=True, fill='both')

    def show_salary_distribution(self):
        plt.figure(figsize=(12, 8))
        top_jobs = self.df['job_title'].value_counts().head(10).index
        sns.boxplot(data=self.df[self.df['job_title'].isin(top_jobs)],
                    x='salary_usd_converted',
                    y='job_title',
                    palette='viridis')
        plt.title('TOP 10 AI职位薪资分布(USD)', fontsize=16)
        plt.xlabel('年薪(USD)', fontsize=12)
        plt.ylabel('职位名称', fontsize=12)
        plt.tight_layout()
        self.show_figure(plt.gcf())

    def show_exp_vs_salary(self):
        plt.figure(figsize=(10, 6))
        sns.regplot(data=self.df, x='years_experience', y='salary_usd_converted',
                    scatter_kws={'alpha': 0.3}, line_kws={'color': 'red'})
        plt.title('工作经验与薪资关系', fontsize=16)
        plt.xlabel('工作经验(年)', fontsize=12)
        plt.ylabel('年薪(USD)', fontsize=12)
        plt.grid(True)
        self.show_figure(plt.gcf())

    def show_company_size_salary(self):
        plt.figure(figsize=(10, 6))
        sns.boxplot(data=self.df, x='company_size', y='salary_usd_converted',
                    order=['S', 'M', 'L'], palette='Set2')
        plt.title('公司规模与薪资水平', fontsize=16)
        plt.xlabel('公司规模(S:小, M:中, L:大)', fontsize=12)
        plt.ylabel('年薪(USD)', fontsize=12)
        self.show_figure(plt.gcf())

    def show_top_skills(self):
        # 提取所有技能并统计频率
        all_skills = []
        for skills in self.df['required_skills']:
            skill_list = [s.strip() for s in skills.split(',')]
            all_skills.extend(skill_list)

        skill_counts = Counter(all_skills)
        top_skills = pd.DataFrame(skill_counts.most_common(15), columns=['Skill', 'Count'])

        plt.figure(figsize=(12, 6))
        sns.barplot(data=top_skills, y='Skill', x='Count', palette='rocket')
        plt.title('TOP 15 热门AI技能需求', fontsize=16)
        plt.xlabel('出现次数', fontsize=12)
        plt.ylabel('技能名称', fontsize=12)
        self.show_figure(plt.gcf())

    def show_skills_wordcloud(self):
        # 提取所有技能并统计频率
        all_skills = []
        for skills in self.df['required_skills']:
            skill_list = [s.strip() for s in skills.split(',')]
            all_skills.extend(skill_list)

        skill_counts = Counter(all_skills)

        wordcloud = WordCloud(width=800, height=400,
                              background_color='white',
                              colormap='viridis').generate_from_frequencies(skill_counts)

        plt.figure(figsize=(15, 8))
        plt.imshow(wordcloud, interpolation='bilinear')
        plt.axis('off')
        plt.title('AI职位技能词云', fontsize=20)
        self.show_figure(plt.gcf())

    def show_global_distribution(self):
        country_counts = self.df['company_location'].value_counts().reset_index()
        country_counts.columns = ['country', 'count']

        fig = px.choropleth(country_counts,
                            locations='country',
                            locationmode='country names',
                            color='count',
                            hover_name='country',
                            color_continuous_scale='Viridis',
                            title='AI职位全球分布热力图')

        # 创建一个新窗口显示Plotly图表
        plotly_window = tk.Toplevel(self.root)
        plotly_window.title("AI职位全球分布热力图")
        plotly_window.geometry("800x600")

        # 添加返回按钮
        back_frame = tk.Frame(plotly_window, bg='#f5f5f5')
        back_frame.pack(fill='x', pady=10)

        back_btn = tk.Button(back_frame, text="关闭",
                             command=plotly_window.destroy,
                             font=("Microsoft YaHei", 12),
                             bg='#1877f2', fg='white',
                             relief='flat', bd=0, padx=20)
        back_btn.pack(side='right', padx=20)

        # 在Tkinter中显示Plotly图表需要额外处理，这里简化为提示
        info_label = tk.Label(plotly_window,
                              text="Plotly图表已在新浏览器窗口打开，请查看浏览器。",
                              font=("Microsoft YaHei", 12))
        info_label.pack(pady=20)

        # 显示Plotly图表
        fig.show()

    def show_remote_work(self):
        remote_counts = self.df['remote_ratio'].value_counts().reset_index()
        remote_counts.columns = ['remote_ratio', 'count']
        remote_counts['remote_type'] = remote_counts['remote_ratio'].map({
            0: '完全办公室',
            50: '混合办公',
            100: '完全远程'
        })

        plt.figure(figsize=(8, 6))
        sns.barplot(data=remote_counts, x='remote_type', y='count', palette='mako')
        plt.title('远程工作类型分布', fontsize=16)
        plt.xlabel('工作类型', fontsize=12)
        plt.ylabel('职位数量', fontsize=12)
        self.show_figure(plt.gcf())

    def show_industry_salary(self):
        plt.figure(figsize=(12, 6))
        sns.boxplot(data=self.df, x='industry', y='salary_usd_converted', palette='Set3')
        plt.title('不同行业的AI职位薪资比较', fontsize=16)
        plt.xlabel('行业', fontsize=12)
        plt.ylabel('年薪(USD)', fontsize=12)
        plt.xticks(rotation=45)
        plt.tight_layout()
        self.show_figure(plt.gcf())

    def show_education_requirements(self):
        edu_order = ['Bachelor', 'Master', 'PhD', 'Associate']
        plt.figure(figsize=(10, 6))
        sns.countplot(data=self.df, y='education_required', order=edu_order, palette='flare')
        plt.title('AI职位学历要求分布', fontsize=16)
        plt.xlabel('职位数量', fontsize=12)
        plt.ylabel('学历要求', fontsize=12)
        self.show_figure(plt.gcf())

    def show_3d_relationship(self):
        fig = plt.figure(figsize=(14, 10))
        ax = fig.add_subplot(111, projection='3d')

        # 准备数据
        x = self.df['years_experience']  # 工作经验(年)
        y = self.df['company_size_encoded']  # 公司规模(0:S, 1:M, 2:L)
        z = self.df['salary_usd_converted'] / 1000  # 薪资(千美元)

        # 创建散点图
        scatter = ax.scatter(x, y, z, c=z, cmap='viridis', s=50, alpha=0.7)

        # 添加颜色条
        cbar = fig.colorbar(scatter, shrink=0.6, aspect=10)
        cbar.set_label('薪资(千美元)', fontsize=12)

        # 设置坐标轴标签
        ax.set_xlabel('工作经验(年)', fontsize=12, labelpad=10)
        ax.set_ylabel('公司规模', fontsize=12, labelpad=10)
        ax.set_zlabel('薪资(千美元)', fontsize=12, labelpad=10)

        # 设置y轴刻度标签
        ax.set_yticks([0, 1, 2])
        ax.set_yticklabels(['小型(S)', '中型(M)', '大型(L)'])

        # 设置视角
        ax.view_init(elev=25, azim=45)

        # 添加标题
        plt.title('AI职位：工作经验、公司规模与薪资的3D关系', fontsize=16, pad=20)

        # 添加网格
        ax.grid(True, linestyle='--', alpha=0.5)

        # 调整布局
        plt.tight_layout()

        self.show_figure(fig)


# 创建主窗口并运行应用
if __name__ == "__main__":
    root = tk.Tk()
    app = LoginSystem(root)
    root.mainloop()